package com.jincp.common.http.okhttp;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.jincp.common.base.BaseApplication;
import com.jincp.common.http.HttpProcessor;
import com.jincp.common.http.HttpRequestParams;
import com.jincp.common.http.IHttpCallback;
import com.jincp.common.http.IHttpProcessor;
import com.jincp.common.okhttp3.progress.*;
import com.jincp.common.utils.HttpsUtils;
import com.jincp.common.utils.MyLogger;

import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import okhttp3.Cache;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

/**
 * =========================================================
 *
 * @ProjectName: OKHTTP网络请求类
 * @ClassName: OkHttpProcessor
 * @Description:
 * @Author: jincp
 * @CreateDate: 2019/9/3 11:52
 * =========================================================
 */
public class OkHttpProcessor implements IHttpProcessor {

    public final static MediaType STREAM = MediaType.parse("application/octet-stream");
    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

    private static final long cacheSize = 1024 * 1024 * 5;//缓存文件最大限制大小5M
    private static String cacheDirectory = BaseApplication.getAppCacheDir().getAbsolutePath();  //设置缓存文件路径
    private static Cache cache = new Cache(new File(cacheDirectory), cacheSize);  //

    private static OkHttpClient okHttpClient = null;
    private Handler handler;

    @Override
    public void init(Context context) {
        if (okHttpClient == null) {
            OkHttpClient.Builder builder = getBuilder(true);
            okHttpClient = builder.build();
        }
        handler = new Handler(Looper.getMainLooper());
    }

    public static void setOkHttpClient(OkHttpClient.Builder builder) {
        if (builder == null) {
            builder = getBuilder(true);
        }
        okHttpClient = builder.build();
    }

    public static OkHttpClient.Builder getBuilder(boolean isHttps) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(HttpProcessor.DEFAULT_CONNECT_TIMEOUT, TimeUnit.MILLISECONDS);
        builder.readTimeout(HttpProcessor.DEFAULT_READ_TIMEOUT, TimeUnit.MILLISECONDS);
        builder.writeTimeout(HttpProcessor.DEFAULT_WRITE_TIMEOUT, TimeUnit.MILLISECONDS);
        // 错误重连
        builder.retryOnConnectionFailure(true);
        // 设置缓存
        builder.cache(cache);
        if (isHttps) {
            HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null);
            builder.sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager);
        }
        return builder;
    }

    @Override
    public void post(Object tag, String url, boolean isProgress, HttpRequestParams params, final IHttpCallback callback) {
        if (okHttpClient == null) {
            throw new NullPointerException("OkHttpUtil.init() first");
        }
        try {
            Headers headers = getRequestHeaders(params);
            RequestBody requestBody = getRequestBody(params);
            if (requestBody != null && isProgress && callback != null) {
                requestBody = new ProgressRequestBody(requestBody, callback);
            }
            Request request = getRequest("POST", url, requestBody, headers, tag);
            actualRequest(request, isProgress, callback);
        } catch (Throwable e) {
            e.printStackTrace();
            callback.onResponseFailure(0, e.getMessage());
        }
    }

    @Override
    public void get(Object tag, String url, boolean isProgress, HttpRequestParams params, final IHttpCallback callback) {
        if (okHttpClient == null) {
            throw new NullPointerException("OkHttpUtil.init() first");
        }
        try {
            if (isProgress) {
                OkHttpClient.Builder builder = okHttpClient.newBuilder().addNetworkInterceptor(new ProgressResponseInterceptor(callback));
                okHttpClient = builder.build();
            }
            url = getFinalUrl(url, params);
            Headers headers = getRequestHeaders(params);
            Request request = getRequest("GET", url, null, headers, tag);
            actualRequest(request, isProgress, callback);
        } catch (Throwable e) {
            e.printStackTrace();
            callback.onResponseFailure(0, e.getMessage());
        }
    }

    @Override
    public void cancelRequest(Object tag) {
        if (okHttpClient == null) {
            throw new NullPointerException("OkHttpUtil.init() first");
        }
        try {
            for (Call call : okHttpClient.dispatcher().queuedCalls()) {
                if (tag.equals(call.request().tag())) {
                    call.cancel();
                }
            }
            for (Call call : okHttpClient.dispatcher().runningCalls()) {
                if (tag.equals(call.request().tag())) {
                    call.cancel();
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    private void actualRequest(Request request, boolean isProgress, final IHttpCallback callback) {
        MyLogger.d("OkHttpProcessor", "actualRequest");
        try {
            Call call = okHttpClient.newCall(request);
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, final IOException e) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            callback.onResponseFailure(0, e.toString());
                        }
                    });
                }

                @Override
                public void onResponse(Call call, final Response response) throws IOException {
                    if (response != null) {
                        try {
                            if (response.isSuccessful()) {
                                final String result = response.body().string();
                                handler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        callback.onResponseSuccess(response.code(), result);
                                    }
                                });
                            } else {
                                final String result = response.message();
                                handler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        callback.onResponseFailure(response.code(), result);
                                    }
                                });
                            }
                            close(response.body());
                        } catch (final Throwable e) {
                            e.printStackTrace();
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    callback.onResponseFailure(response.code(), e.getMessage());
                                }
                            });
                            close(response.body());
                        }
                    } else {
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                callback.onResponseFailure(0, "response == null");
                            }
                        });
                    }
                }
            });
        } catch (Throwable e) {
            e.printStackTrace();
            callback.onResponseFailure(0, e.getMessage());
        }
    }


    protected final void close(ResponseBody body) {
        if (body == null) return;
        try {
            body.close();
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }


    private Request getRequest(String method, String url, RequestBody requestBody, Headers headers, Object tag) {
        Request.Builder builder = new Request.Builder();
        if (requestBody != null) {
            if (method.equals("POST")) {
                builder.post(requestBody);
            } else if (method.equals("PUT")) {
                builder.put(requestBody);
            } else if (method.equals("DELETE")) {
                builder.delete(requestBody);
            }
        }
        if (headers != null) {
            builder.headers(headers);
        }
        if (tag != null) {
            builder.tag(tag);
        }
        builder.url(url);
        return builder.build();
    }

    private RequestBody getRequestBody(HttpRequestParams params) {
        try {
            if (!params.getJsonParams().isEmpty()) {
                return createJsonBuilderBody(params.getJsonParams());
            } else if (!params.getFileParams().isEmpty()) {
                return createMultipartBuilderBody(params);
            } else {
                return createEncodingBuilderBody(params.getUrlParams());
            }
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        }
    }

    private RequestBody createJsonBuilderBody(Map<String, Object> jsonParams) {
        //创建一个RequestBody(参数1：数据类型 参数2传递的json串)
        Gson gson = new GsonBuilder().create();
        String jsonString = gson.toJson(jsonParams);
        return RequestBody.create(JSON, jsonString);
    }

    private RequestBody createMultipartBuilderBody(HttpRequestParams params) throws Throwable {
        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);
        for (ConcurrentHashMap.Entry<String, String> entry : params.getUrlParams().entrySet()) {
            builder.addFormDataPart(entry.getKey(), entry.getValue());
        }
        for (ConcurrentHashMap.Entry<String, File> entry : params.getFileParams().entrySet()) {
            File file = entry.getValue();
            if (file == null) continue;
            builder.addFormDataPart(entry.getKey(), file.getName(), RequestBody.create(STREAM, file));
        }
        return builder.build();
    }

    private RequestBody createEncodingBuilderBody(Map<String, String> urlParams) throws Throwable {
        FormBody.Builder builder = new FormBody.Builder();
        for (ConcurrentHashMap.Entry<String, String> entry : urlParams.entrySet()) {
            // 添加还未被encode的参数
            builder.add(entry.getKey(), entry.getValue());
        }
        return builder.build();
    }


    private String getFinalUrl(String url, HttpRequestParams params) {
        if (params != null) {
            String paramString = params.getUrlParamString().trim();
            if (!paramString.equals("") && !paramString.equals("?")) {
                url += url.contains("?") ? "&" : "?";
                url += paramString;
            }
        }
        return url;
    }

    private Headers getRequestHeaders(HttpRequestParams params) {
        if (params != null) {
            try {
                return Headers.of(params.getHeaderParams());
            } catch (Throwable e) {
                e.printStackTrace();
                return null;
            }
        }
        return null;
    }
}
